#ifdef _PLATFORM_WII
#include <Revolution.h>
#include <revolution/kpad.h>
#include <revolution/sc.h>

#include <demo.h>
#include <string.h>
#include <math.h>

#include <revolution/hbm.h>

#include "util/mmgr.h"

enum
{
    OFF = 0,
    ON
};

enum
{
    eAlphaInIcon = 0,
    ePauseIcon,
    eAlphaOutIcon
};

static const f32 scStickMoveCoe =  2048.0f/72.0f; /* Amount-of-movement coefficient of the analog stick */
static KPADStatus sKpads[ WPAD_MAX_CONTROLLERS ][ KPAD_MAX_READ_BUFS ];
static KPADStatus sKpadToHBM[ WPAD_MAX_CONTROLLERS ];

static TPLPalettePtr sIconTpl;

/* Allocate a buffer for sounds */
u8* sound_buf;
/* Read destination for the sound data from the optical disc */
u8* sound_data;

// Reset/Power Callback
static void ResetCallback();
static void PowerCallback();
static bool reset_called,power_called;

// NTSC / PAL
int sTvmode;

// Frame buffer size
static const u16 sFbWidth = 608;
static const u16 sFbHeight = 456;

// VI size
static const u16 viWidth = 670;
static const u16 viWidthWide = 686;
static const u16 viWidthWide_Pal = 682;

static const u16 viHeight = 456;
static const u16 viHeight_Pal = 542;

#ifdef  ENABLE_BALANCE_BOARD
static u8  workarea[WPAD_BLCINT_WORK_LEN] ATTRIBUTE_ALIGN(32);
#endif // ENABLE_BALANCE_BOARD
/* RenderMode */
static GXRenderModeObj sRMObj[2] =
{
    /* NTSC 4:3 */
    {
        VI_TVMODE_NTSC_INT,
        sFbWidth,
        sFbHeight,
        viHeight,
        (u16)((VI_MAX_WIDTH_NTSC - viWidth)/2),
        (u16)((VI_MAX_HEIGHT_NTSC - viHeight)/2),
        viWidth,
        viHeight,
        VI_XFBMODE_DF,
        GX_FALSE,
        GX_FALSE,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        8, 8,
        10, 12, 10,
        8, 8,
    },
    /* PAL 4:3 */
    {
        VI_TVMODE_PAL_INT,
        sFbWidth,
        sFbHeight,
        viHeight_Pal,
        (u16)((VI_MAX_WIDTH_PAL - viWidth)/2),
        (u16)((VI_MAX_HEIGHT_PAL - viHeight_Pal)/2),
        viWidth,
        viHeight_Pal,
        VI_XFBMODE_DF,
        GX_FALSE,
        GX_FALSE,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        8, 8,
        10, 12, 10,
        8, 8,
    }
};

static GXRenderModeObj sRMObjWide[2] =
{
    /* NTSC 16:9 */
    {
        VI_TVMODE_NTSC_INT,
        sFbWidth,
        sFbHeight,
        viHeight,
        (u16)((VI_MAX_WIDTH_NTSC - viWidthWide)/2),
        (u16)((VI_MAX_HEIGHT_NTSC - viHeight)/2),
        viWidthWide,
        viHeight,
        VI_XFBMODE_DF,
        GX_FALSE,
        GX_FALSE,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        8, 8,
        10, 12, 10,
        8, 8,
    },
    /* PAL 16:9 */
    {
        VI_TVMODE_PAL_INT,
        sFbWidth,
        sFbHeight,
        viHeight_Pal,
        (u16)((VI_MAX_WIDTH_PAL - viWidthWide_Pal)/2),
        (u16)((VI_MAX_HEIGHT_PAL - viHeight_Pal)/2),
        viWidthWide_Pal,
        viHeight_Pal,
        VI_XFBMODE_DF,
        GX_FALSE,
        GX_FALSE,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        6, 6, 6, 6, 6, 6,
        8, 8,
        10, 12, 10,
        8, 8,
    }
};

static void* allocMem1( u32 size )
{
	return WiiMMGR_Alloc(HEAP_MEM1, size, DEFAULT_ALIGNMENT);
}

static u8 freeMem1( void* ptr )
{
	WiiMMGR_Free(ptr);
    return 1;
}

static void* allocMem2( u32 size )
{
	return WiiMMGR_Alloc(HEAP_MEM2, size, DEFAULT_ALIGNMENT);
}

static u8 freeMem2( void* ptr )
{
	WiiMMGR_Free(ptr);
    return 1;
}

static int SoundCallback( int evt, int arg )
{
    OSReport( "SoundCallback: %d, %d\n", evt, arg );
    return HBMSEV_RET_NONE;
}

static void ResetCallback()
{
    reset_called = true;
}

static void PowerCallback()
{
    power_called = true;
}

static void* ReadDvdFile(
    const char*     fileName,
    MEMAllocator*   mem,
    u32* fileSize
)
{
    u32 fileLen, fileLenUp32;
    void* readBuf;
    s32 readBytes;

    DVDFileInfo fileInfo;
    if (! DVDOpen(fileName, &fileInfo))
    {
        return NULL;
    }

    fileLen     = DVDGetLength(&fileInfo);
    if( (fileLen % 32) != 0 )
    {
        fileLenUp32 = fileLen + (32 - (fileLen % 32));
    }
    else
    {
        fileLenUp32 = fileLen;
    }
    readBuf = MEMAllocFromAllocator(mem, fileLenUp32);
    readBytes = DVDRead(&fileInfo, readBuf, (s32)(fileLenUp32), 0);
    ASSERT(readBytes > 0);
    if( fileSize )
        *fileSize   = fileLen;
    DVDClose(&fileInfo);

    return readBuf;
}


static void SetProjection( int wideflag )
{
    Mtx44 projMtx;
    

    if( !wideflag )
    {
        DEMOReInit(&sRMObj[sTvmode]);
        f32 viRateX = sRMObj[sTvmode].viWidth  / 670.f;
        f32 viRateY = sRMObj[sTvmode].viHeight / 456.f;
        MTXOrtho(projMtx, viRateY * 228.0f, -viRateY * 228.0f, -viRateX * 304.0f, viRateX * 304.0f, 0.0f, 500.0f);
    }
    else
    {
        DEMOReInit(&sRMObjWide[sTvmode]);
        f32 viRateX = sRMObjWide[sTvmode].viWidth  / 686.f;
        f32 viRateY = sRMObjWide[sTvmode].viHeight / 456.f;
        MTXOrtho(projMtx, viRateY * 228.0f, -viRateY * 228.0f, -viRateX * 416.0f, viRateX * 416.0f, 0.0f, 500.0f);
    }

    GXSetProjection(projMtx, GX_ORTHOGRAPHIC);
}

static void InitGX()
{
    GXClearVtxDesc();

    GXSetVtxAttrFmt(GX_VTXFMT4, GX_VA_POS,  GX_POS_XY,  GX_F32, 0);
    GXSetVtxAttrFmt(GX_VTXFMT4, GX_VA_CLR0, GX_CLR_RGB, GX_RGB8, 0);
    GXSetVtxDesc(GX_VA_POS,  GX_DIRECT);
    GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);

    GXSetNumChans(1);
    GXSetNumTexGens(0);
    GXSetNumTevStages(1);
    GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
    GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR);

    GXSetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR);
    GXSetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
    GXSetCurrentMtx( GX_PNMTX1 );
}

static void DrawBanIcon( u8 alpha )
{
    GXTexObj texObj;
    Mtx      view_mtx ;
    // mtx
    MTXIdentity( view_mtx ) ;
    GXLoadPosMtxImm( view_mtx, GX_PNMTX1 ) ;
    GXSetCurrentMtx( GX_PNMTX1 ) ;

    GXClearVtxDesc();

    GXSetVtxAttrFmt(GX_VTXFMT5, GX_VA_POS,  GX_POS_XY,  GX_S16, 0);
    GXSetVtxAttrFmt(GX_VTXFMT5, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
    GXSetVtxDesc(GX_VA_POS,  GX_DIRECT);
    GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);

    GXSetNumChans(1);
    GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);

    GXSetNumTexGens(1);
    GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);

    GXSetNumTevStages(1);
    GXSetTevColor(GX_TEVREG0, (GXColor){255, 255, 255, alpha});
    GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
    GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
    GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
    GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_A0, GX_CA_TEXA, GX_CA_ZERO);
    GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);

    GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
    GXSetZMode(GX_FALSE, GX_LEQUAL, GX_FALSE);

    TPLGetGXTexObjFromPalette( sIconTpl, &texObj, 0 );
    GXLoadTexObj( &texObj, GX_TEXMAP0 );

    /* The rendering position may be anywhere inside the safe frame. */
    GXBegin( GX_QUADS, GX_VTXFMT5, 4 );
    GXPosition2s16( -256 + 0, 188 - 56 );
    GXTexCoord2s16( 0, 1 );
    GXPosition2s16( -256 + 0, 188 + 0 );
    GXTexCoord2s16( 0, 0 );
    GXPosition2s16( -256 + 56, 188 + 0 );
    GXTexCoord2s16( 1, 0 );
    GXPosition2s16( -256 + 56, 188 - 56 );
    GXTexCoord2s16( 1, 1 );
    GXEnd();
}

static void InitHomeButtonInfo( HBMDataInfo* pHbmInfo )
{
    char dirName[] = "HomeButton2";

    char nameBuf[32];

    /* Create filename */
    strcpy( nameBuf, dirName );
    /* Switching as necessary according to the language setting */
    pHbmInfo->region=SCGetLanguage();
    switch (pHbmInfo->region)
    {
    case SC_LANG_JAPANESE:
        strcat( nameBuf, "/homeBtn.arc" );
        break;
    case SC_LANG_ENGLISH:
        strcat( nameBuf, "/homeBtn_ENG.arc" );
        break;
    case SC_LANG_GERMAN:
        strcat( nameBuf, "/homeBtn_GER.arc" );
        break;
    case SC_LANG_FRENCH:
        strcat( nameBuf, "/homeBtn_FRA.arc" );
        break;
    case SC_LANG_SPANISH:
        strcat( nameBuf, "/homeBtn_SPA.arc" );
        break;
    case SC_LANG_ITALIAN:
        strcat( nameBuf, "/homeBtn_ITA.arc" );
        break;
    case SC_LANG_DUTCH:
        strcat( nameBuf, "/homeBtn_NED.arc" );
        break;
#ifdef SC_LANG_SIMP_CHINESE
    case SC_LANG_SIMP_CHINESE:
        strcat( nameBuf, "/homeBtn_CHN.arc" );
        break;
#endif
#ifdef SC_LANG_KOREAN
    case SC_LANG_KOREAN:
        strcat( nameBuf, "/homeBtn_KOR.arc" );
        break;
#endif
    default:
        pHbmInfo->region=SC_LANG_JAPANESE;
        strcat( nameBuf, "/homeBtn.arc" );
        break;
    }
    pHbmInfo->layoutBuf = ReadDvdFile( nameBuf, &DemoAllocator1, NULL );

    strcpy( nameBuf, dirName );
    strcat( nameBuf, "/SpeakerSe.arc" );
    pHbmInfo->spkSeBuf = ReadDvdFile( nameBuf, &DemoAllocator1, NULL );

    strcpy( nameBuf, dirName );
#ifdef HBM_NO_SAVE
    strcat( nameBuf, "/home_nosave.csv" );
#else
    strcat( nameBuf, "/home.csv" );
#endif
    pHbmInfo->msgBuf = ReadDvdFile( nameBuf, &DemoAllocator1, NULL );

    strcpy( nameBuf, dirName );
    strcat( nameBuf, "/config.txt" );
    pHbmInfo->configBuf = ReadDvdFile( nameBuf, &DemoAllocator1, &pHbmInfo->configBufSize );

    pHbmInfo->sound_callback = SoundCallback;
    pHbmInfo->backFlag       = OFF;
    pHbmInfo->cursor         = 0;
    pHbmInfo->adjust.x       = 1.0f;
    pHbmInfo->adjust.y       = 1.0f;
    pHbmInfo->frameDelta     = 1.0f;

    /* Memory allocation settings */
    pHbmInfo->mem = allocMem1( HBM_MEM_SIZE );
    pHbmInfo->memSize = HBM_MEM_SIZE;
    pHbmInfo->pAllocator = NULL;
    
    /* Change the exit message each time the HOME menu is started */
    pHbmInfo->messageFlag++;
    pHbmInfo->messageFlag &= 0x3;
}

/* Initialize sounds */
static void InitSound()
{
	const char*	Sound = "HomeButton2/HomeButtonSe.arc";
    
    /* Load sound data for AX use */
    sound_data = (u8*)ReadDvdFile( Sound, &DemoAllocator1, NULL );
    /* Allocate a buffer for sounds */
    sound_buf = (u8*)allocMem1( HBM_MEM_SIZE_SOUND );
    HBMCreateSound( sound_data, sound_buf, HBM_MEM_SIZE_SOUND );
}

/* Cursor position initialization */
static void InitControllerData( HBMControllerData* pConData )
{
    int i;
    for( i = 0; i < WPAD_MAX_CONTROLLERS; i++ )
    {
        pConData->wiiCon[i].pos.x = 0.f;
        pConData->wiiCon[i].pos.y = 0.f;
        pConData->wiiCon[i].use_devtype = WPAD_DEV_CORE;
    }
}

/* Absolute value clamp */
static f32 AbsClamp( f32 val, f32 max )
{
    return ( ( val > max ) ? max : ( val < -max ) ? -max : val );
}

/* Cursor movement processing for analog stick */
static int calcAnalogCursorPos( f32 stickX, f32 stickY, Vec2* pos )
{
    f32 x,y;
    x = ( stickX / scStickMoveCoe );
    y = ( stickY / scStickMoveCoe );
    x = AbsClamp( x, 1.0f );
    y = AbsClamp( y, 1.0f );
    if( x == 0.0f && y == 0.0f ) return FALSE;
    pos->x = AbsClamp( pos->x + x, 1.0f );
    pos->y = AbsClamp( pos->y - y, 1.0f );
    return TRUE;
}

/* Cursor movement processing when using +Control key */
static int calcDigitalCursorPos( u32 button, Vec2* pos )
{
    const float spd =1.0f / scStickMoveCoe;
    const float spd2= spd * 0.7071f;

    button&=KPAD_CL_BUTTON_UP|KPAD_CL_BUTTON_LEFT|KPAD_CL_BUTTON_DOWN|KPAD_CL_BUTTON_RIGHT;
    switch (button)
    {
    case KPAD_CL_BUTTON_UP:     pos->y-=spd; break;
    case KPAD_CL_BUTTON_LEFT:   pos->x-=spd; break;
    case KPAD_CL_BUTTON_DOWN:   pos->y+=spd; break;
    case KPAD_CL_BUTTON_RIGHT:  pos->x+=spd; break;
    case KPAD_CL_BUTTON_UP  |KPAD_CL_BUTTON_LEFT:   pos->y-=spd2; pos->x-=spd2; break;
    case KPAD_CL_BUTTON_UP  |KPAD_CL_BUTTON_RIGHT:  pos->y-=spd2; pos->x+=spd2; break;
    case KPAD_CL_BUTTON_DOWN|KPAD_CL_BUTTON_LEFT:   pos->y+=spd2; pos->x-=spd2; break;
    case KPAD_CL_BUTTON_DOWN|KPAD_CL_BUTTON_RIGHT:  pos->y+=spd2; pos->x+=spd2; break;
    default: return FALSE;
    }
    pos->x = AbsClamp( pos->x, 1.0f );
    pos->y = AbsClamp( pos->y, 1.0f );
    return TRUE;
}


/* Change the adjust value depending on the display mode */
static void SetAdjustValue( HBMDataInfo* pHbmInfo, int wideflag )
{
    if( !wideflag )
    {
        /* 4:3 */
        pHbmInfo->adjust.x       = 1.0f;
        pHbmInfo->adjust.y       = 1.0f;
    }
    else
    {
        /* 16:9 */
        pHbmInfo->adjust.x       = 832.f / 608.f;
        pHbmInfo->adjust.y       = 1.0f;
    }
    
    if(sTvmode == 0)
    {
        /* NTSC: 60Hz */
        pHbmInfo->frameDelta     = 1.0f;
    }
    else
    {
        /* PAL: 50Hz */
        pHbmInfo->frameDelta     = 1.2f;
    }
}

void		StartupHBM()
{
    OSInitFastCast();

	// setup Icon
	const char*		Icon = "HomeButton2/homeBtnIcon.tpl";
    sIconTpl = ( TPLPalettePtr )ReadDvdFile( Icon, &DemoAllocator1, NULL );
    TPLBind( sIconTpl );

    OSSetResetCallback(ResetCallback);
    OSSetPowerCallback(PowerCallback);

    reset_called = false;
    power_called = false;

}


void		ShutdownHBM()
{

}

void		UpdateHBM()
{
    Mtx mv;
    HBMDataInfo hbmInfo;
    HBMControllerData conData;
    int homeBtnSwitch  = OFF;   /* HOME button switch */
    int drawModeFlag   = OFF;   /* Flag for toggling between 4:3 and 16:9 displays */
    int banIconSwitch  = OFF;   /* HOME button prohibited icon */
    s8  banIconMode    = 0;     /* 0: AlphaIn, 1: Pause, 2: AlphaOut */
    OSTick banIconTime = 0;
    u8  banIconAlpha   = 0;
    s32 wpad_result[WPAD_MAX_CONTROLLERS];
    u32 pad_type[WPAD_MAX_CONTROLLERS];
    Vec2 pos[WPAD_MAX_CONTROLLERS];/* Position of the Pointer */
    int input_classic;
    int i;

    s32 kpad_read[WPAD_MAX_CONTROLLERS];
    GXRenderModeObj* pRm;



    /* Wii controllers */
    for( int i = 0; i < WPAD_MAX_CONTROLLERS; i++ )
    {
        wpad_result[i] = WPADProbe( i, &pad_type[i] );
        conData.wiiCon[i].use_devtype = pad_type[i];
        kpad_read[i] = KPADRead( i, &sKpads[i][0], KPAD_MAX_READ_BUFS );
        if( kpad_read[i] )
        	memcpy( &sKpadToHBM[i], &sKpads[i][0], sizeof(KPADStatus) );
        
        switch( wpad_result[i] )
        {
        /* In the following error states, the value gotten by KPADRead is applied as is. */
        case WPAD_ERR_BUSY:
        case WPAD_ERR_TRANSFER:
        case WPAD_ERR_INVALID:
        case WPAD_ERR_CORRUPTED:
        case WPAD_ERR_NONE:
            conData.wiiCon[i].kpad = &sKpadToHBM[i];
            
            {
                /* 
                According to guidelines, if there is input from a Classic Controller, that input is prioritized and inherits DPD coordinates.
                
                Specify the DPD absolute coordinates when there is no Classic Controller input.
                */
                
                input_classic = calcDigitalCursorPos(
                    conData.wiiCon[i].kpad->ex_status.cl.hold,
                    &conData.wiiCon[i].pos );
                
                
                input_classic = input_classic | calcAnalogCursorPos(
                    conData.wiiCon[i].kpad->ex_status.cl.lstick.x,
                    conData.wiiCon[i].kpad->ex_status.cl.lstick.y,
                    &conData.wiiCon[i].pos );
                    
                if( !input_classic && conData.wiiCon[i].kpad->dpd_valid_fg > 0)
                {
                    conData.wiiCon[i].pos.x = conData.wiiCon[i].kpad->pos.x;
                    conData.wiiCon[i].pos.y = conData.wiiCon[i].kpad->pos.y;
                }
            }

            /* Change the rendering mode */
            if( !homeBtnSwitch && sKpadToHBM[i].trig == KPAD_BUTTON_2 )
            {
                drawModeFlag = !drawModeFlag;
                SetProjection( drawModeFlag );
                SetAdjustValue( &hbmInfo, drawModeFlag );
                pRm = DEMOGetRenderModeObj();
            }

            if( sKpadToHBM[i].trig == KPAD_BUTTON_1 )
            {
                VISetBlack(FALSE);
                VIFlush();
            }

            if ( !homeBtnSwitch && !banIconSwitch && sKpadToHBM[i].trig == KPAD_BUTTON_PLUS )
            {
                banIconMode   = eAlphaInIcon;
                banIconSwitch = ON;
                banIconTime   = OSGetTick();
                banIconAlpha  = 0;
            }
            break;
        /* Apply NULL in the following error states. */
        case WPAD_ERR_NO_CONTROLLER:
        default:
            conData.wiiCon[i].kpad = NULL;
            memset(&sKpadToHBM[i], 0, sizeof(KPADStatus));
            break;
        }
    }
    if( !homeBtnSwitch && !banIconSwitch )
    {
        BOOL press_home = FALSE;
        
        for ( int i = 0; i < WPAD_MAX_CONTROLLERS; i++ )
        {
             if ( WPAD_ERR_NONE != wpad_result[i] ) continue;
             
             /* When HOME is pressed on the Wii Remote or Classic Controller */
             if ( sKpadToHBM[i].trig == KPAD_BUTTON_HOME || 
                  sKpadToHBM[i].ex_status.cl.trig==KPAD_CL_BUTTON_HOME )
             {
                 press_home = TRUE;
                 homeBtnSwitch = ON;
                 break;
             }
        }
        if ( press_home )
        {
            InitControllerData( &conData );
            InitHomeButtonInfo( &hbmInfo );
            /* Set the adjust value depending on the screen mode */
            SetAdjustValue( &hbmInfo, drawModeFlag );
            
            /* HBM initialization */
            HBMCreate( &hbmInfo );
            HBMInit();
            /* Adjust ON */
            HBMSetAdjustFlag( TRUE );

            /* Load sounds */
            InitSound();
            
            OSReport("Mem1Free = %d\n", MEMGetTotalFreeSizeForExpHeap((MEMHeapHandle)DemoAllocator1.pHeap) );
            OSReport("Mem2Free = %d\n", MEMGetTotalFreeSizeForExpHeap((MEMHeapHandle)DemoAllocator2.pHeap) );
        }
    }

    if( homeBtnSwitch )
    {
        /* Update SE (sound effect) */
        HBMUpdateSound();
        
        /* Update the HOME Menu */
        if( HBMCalc( &conData ) >= HBM_SELECT_HOMEBTN )
        {
            /* The number of the decided-upon button is returned */
            OSReport("Select Btn:%d\n", HBMGetSelectBtnNum());
            OSReport("Reassigned:%d\n", HBMIsReassignedControllers());
            
            
            /* Process executed when returning from the HOME Menu */
            switch( HBMGetSelectBtnNum() )
            {
            case HBM_SELECT_HOMEBTN:
                break;
            /* Move to the Wii Menu */
            case HBM_SELECT_BTN1:
                OSReport( "Return to WiiMenu.\n" );
                OSReturnToMenu();
                break;
            /* Reset */
            case HBM_SELECT_BTN2:
                OSReport( "Reset.\n" );
                OSRestart( 0 );
                break;
            case 3:
                
                break;
            default:
                break;
            }
            /* Release various items */
            HBMDelete( );
            HBMDeleteSound();
            
            freeMem1( sound_buf );
            freeMem1( sound_data );
            freeMem1( hbmInfo.mem );
            freeMem1( hbmInfo.layoutBuf );
            freeMem1( hbmInfo.spkSeBuf );
            freeMem1( hbmInfo.msgBuf );
            freeMem1( hbmInfo.configBuf );
            
            homeBtnSwitch = OFF;
            
            OSReport("Mem1Free = %d\n", MEMGetTotalFreeSizeForExpHeap((MEMHeapHandle)DemoAllocator1.pHeap) );
            OSReport("Mem2Free = %d\n", MEMGetTotalFreeSizeForExpHeap((MEMHeapHandle)DemoAllocator2.pHeap) );
        }
        
    }

    /* Calculate the Pointer position */
    for( i = 0; i < PAD_MAX_CONTROLLERS; i++ )
    {
        /* For Wii */
        if ( WPAD_ERR_NONE == wpad_result[i] &&
             0 < kpad_read[i] &&
             conData.wiiCon[i].kpad )
        {
            if( !homeBtnSwitch )
            {
                if( sKpads[i]->dev_type == WPAD_DEV_CLASSIC )
                {
                    pos[i].x = conData.wiiCon[i].pos.x;
                    pos[i].y = conData.wiiCon[i].pos.y;
                }
                else
                {
                    pos[i].x = conData.wiiCon[i].kpad->pos.x;
                    pos[i].y = conData.wiiCon[i].kpad->pos.y;
                }

                pos[i].x *= pRm->fbWidth * 0.5f;
                pos[i].y *= pRm->xfbHeight * 0.5f;

                if( drawModeFlag )
                {
                    pos[i].x *= hbmInfo.adjust.x;
                    pos[i].y *= hbmInfo.adjust.y;
                }
            }
        }
    }

    DEMOBeforeRender();
    {
        InitGX();
        if( homeBtnSwitch )
        {
            /* Render the HOME Menu */
            HBMDraw();
        }
        /* Render the specified cursor inside the HOME Menu */
        if( !homeBtnSwitch )
        {
            for( i = 0; i < PAD_MAX_CONTROLLERS; i++ )
            {
                if( conData.wiiCon[i].kpad )
                {
                    /* Render the Wii Pointer */
                    GXBegin( GX_QUADS, GX_VTXFMT4, 4 );
                    GXPosition2f32( -10 + pos[i].x, -10 - pos[i].y );
                    GXColor3u8( 255, 255, 255 );
                    GXPosition2f32( -10 + pos[i].x,  10 - pos[i].y );
                    GXColor3u8( 255, 255, 255 );
                    GXPosition2f32(  10 + pos[i].x,  10 - pos[i].y );
                    GXColor3u8( 255, 255, 255 );
                    GXPosition2f32(  10 + pos[i].x, -10 - pos[i].y );
                    GXColor3u8( 255, 255, 255 );
                    GXEnd();
                }
            }

            /* Render the HOME Menu prohibited icon */
            if ( banIconSwitch )
            {
                f32 elapse = OSTicksToMilliseconds( OSDiffTick( OSGetTick(), banIconTime ) );

                switch ( banIconMode )
                {
                case eAlphaInIcon: /* AlphaIn (250ms) */
                    banIconAlpha = ( u8 )( 255.9f * ( elapse / 250.f ) );
                    if ( elapse >= 250.f )
                    {
                        banIconTime  = OSGetTick();
                        banIconMode  = ePauseIcon;
                        banIconAlpha = 255;
                    }
                    break;
                case ePauseIcon: /* Pause (1000ms) */
                    if ( elapse >= 1000.f )
                    {
                        banIconTime = OSGetTick();
                        banIconMode = eAlphaOutIcon;
                    }
                    break;
                case eAlphaOutIcon: /* AlphaOut (250ms) */
                    banIconAlpha = ( u8 )( 255.9f * ( ( 250.f - elapse ) / 250.f ) );
                    if ( elapse >= 250.f )
                    {
                        banIconAlpha  = 0;
                        banIconSwitch = OFF;
                    }
                    break;
                }

                DrawBanIcon( banIconAlpha );
            }
        }
    }
    DEMODoneRender();
    
    /* Process executed when the RESET or Power Button is pressed */
    if(reset_called)
    {
        /*When other than the HOME Menu, go straight on to reset */
        if( homeBtnSwitch == OFF )
        {
            OSRestart(0);
        }
        /* If the HOME Menu is running, reset after black-out */
        else
        {
            HBMStartBlackOut();
        }
        reset_called = false;
    }
    
    if(power_called)
    {
        OSReturnToMenu();
    }
}
#endif
